<template>
  <div class="rev-table">
    <div class="rev-card el-card el-card__body" style="padding-bottom: 0">
      <div class="table-title" v-if="config.title">{{ config.title }}</div>
      <div class="rev-query-form" v-query-expand ref="revQueryForm" v-if="columns?.length > 0">
        <grid-form
          v-if="pageQuery && pageQuery.grid"
          ref="queryMeta"
          v-model="conditions"
          :fields="columns"
          :page="pageQuery"
          module="query"
        >
          <el-button size="default" type="primary" class="ml10" @click="getList()">
            查询
          </el-button>
          <el-button size="default" class="ml10" @click="resetQuery()"> 重置 </el-button>
        </grid-form>
        <meta-query v-else ref="queryMeta" v-model="conditions" :source="source" :meta="columns">
          <el-button size="default" type="primary" class="ml10" @click="getList()">
            查询
          </el-button>
          <el-button size="default" class="ml10" @click="resetQuery()"> 重置 </el-button>
        </meta-query>
      </div>
    </div>
    <div class="rev-card el-card el-card__body" style="flex: 1">
      <div class="query-tools" v-if="source && operations">
        <vxe-toolbar ref="toolbarRef" custom>
          <template #buttons>
            <template v-for="op in operations" :key="op.key">
              <el-upload
                v-if="op.key === 'import'"
                ref="uploadRef"
                class="query-upload"
                action="/api/table/import"
                :auto-upload="true"
                :show-file-list="false"
                :data="{ key: 'import', source }"
                v-auths="getPermissions(op?.permissions)"
                :on-success="onUploadSuccess"
              >
                <template #trigger>
                  <el-button class="op-btn"
                    ><img :src="op.icon" /><span class="text">{{ op.name }}</span></el-button
                  >
                </template>
              </el-upload>
              <el-dropdown
                v-else-if="op.type === 'search_export' && hasPermissions(op?.permissions)"
                :disabled="op._loading"
                split-button
                size="default"
                @click="downloadList('page', op)"
                @command="(cmd) => downloadList(cmd, op)"
                style="margin-right: 12px"
              >
                导出
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item command="page">导出当前页</el-dropdown-item>
                    <el-dropdown-item command="select">导出选择数据</el-dropdown-item>
                    <el-dropdown-item command="all">导出全部</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
              <el-button
                v-else-if="op.bytable"
                v-hasPermi="op.roles"
                v-auths="getPermissions(op?.permissions)"
                :loading="op._loading"
                class="op-btn"
                :title="op.hint"
                @click="doOperation(op)"
              >
                <img v-if="!op._loading" :src="op.icon" /><span class="text">{{
                  op.name
                }}</span></el-button
              >
            </template>
          </template>
          <template #tools>
            <el-tooltip class="box-item" effect="light" placement="left-end">
              <el-badge
                :hidden="!useCrossSelection"
                :value="crossSelectionList.length"
                :offset="[-5, 0]"
                style="margin-right: 18px"
              >
                <el-button
                  circle
                  :type="useCrossSelection ? 'primary' : 'default'"
                  @click="useCrossSelection = !useCrossSelection"
                >
                  <Icon icon="svg-icon:list-select" :size="22" />
                </el-button>
              </el-badge>
              <template #content>
                <template v-if="useCrossSelection">
                  <div>
                    已选
                    <span style="font-weight: bold; color: var(--el-color-success)">
                      {{ crossSelectionList.length }}
                    </span>
                    条
                  </div>
                  <el-button size="small" @click="crossSelectionList = []">清空</el-button>
                  <el-button size="small" type="primary" @click="crossSelectionDialogVisible = true"
                    >查看</el-button
                  >
                </template>
                <div v-else> 点击开启跨页选择 </div>
              </template>
            </el-tooltip>
          </template>
        </vxe-toolbar>
      </div>
      <div class="main-table">
        <meta-table
          ref="metaTableRef"
          v-model:selection="multipleSelection"
          :source="source"
          :meta="pageColumns"
          :operations="operations"
          :list="dataList"
          :mergeCells="mergeCellsData"
          :slotColumns="slotColumns"
          :showSelect="!!(operations && operations.some((o) => o.bytable))"
          :showOperation="showOperation"
          :config="{ ...tableConfig, height: singlePage ? 'auto' : 'unset' }"
          @delete-row="showDelete"
          @edit-row="showEdit"
          @edit-save="editSave"
          @sort-change="sortChange"
          @cell-operation="onCellOperation"
          @selection-change="multipleSelectionChange"
        >
          <template #operator="scope">
            <!-- 移动端，大于等于2个按钮，就变下拉按钮 -->
            <template v-if="isMobile && getShowButtons(scope.row).length >= 2">
              <el-dropdown style="vertical-align: middle">
                <span class="el-dropdown-link">
                  操作
                  <el-icon class="el-icon--right">
                    <svg-icon name="ele-ArrowDown" />
                  </el-icon>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                    <template v-for="op in getShowButtons(scope.row)" :key="op.key">
                      <el-dropdown-item
                        :disabled="op._loading || isLoading || checkDisabled(op, scope.row)"
                        @click.stop="doOperation(op, scope.row)"
                        >{{ op.name }}</el-dropdown-item
                      >
                    </template>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </template>
            <!-- PC端，大于3个按钮，就变下拉按钮 -->
            <template v-else-if="getShowButtons(scope.row).length > collapseButtonCount">
              <template v-for="(op, i) in getShowButtons(scope.row)">
                <el-button
                  :key="op.key"
                  link
                  type="primary"
                  :disabled="op._loading || isLoading || checkDisabled(op, scope.row)"
                  style="vertical-align: middle"
                  v-if="i < collapseButtonCount - 1"
                  @click.stop="doOperation(op, scope.row)"
                >
                  {{ op.name }}
                </el-button>
              </template>
              <el-dropdown style="vertical-align: middle">
                <span class="el-dropdown-link">
                  更多
                  <el-icon class="el-icon--right">
                    <svg-icon name="ele-ArrowDown" />
                  </el-icon>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                    <template v-for="(op, i) in getShowButtons(scope.row)" :key="op.key">
                      <el-dropdown-item
                        :disabled="op._loading || isLoading || checkDisabled(op, scope.row)"
                        v-if="i >= collapseButtonCount - 1"
                        @click.stop="doOperation(op, scope.row)"
                        >{{ op.name }}</el-dropdown-item
                      >
                    </template>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </template>
            <template v-else v-for="op in getShowButtons(scope.row)" :key="op.key">
              <el-button
                link
                type="primary"
                :disabled="op._loading || isLoading || checkDisabled(op, scope.row)"
                @click.stop="doOperation(op, scope.row)"
              >
                {{ op.name }}
              </el-button>
            </template>
          </template>
          <template #column="scope" v-if="slotColumns && slotColumns.length > 0">
            <slot name="column" :row="scope.row" :column="scope.column"></slot>
          </template>
        </meta-table>
      </div>
      <div class="table-pagination" v-if="source || total > 0">
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="query.page"
          :page-sizes="[10, 20, 50, 100]"
          :page-size="query.size"
          :layout="isMobile ? 'total, prev, pager, next' : 'total, slot, prev, pager, next, jumper'"
          :pager-count="isMobile ? 5 : 7"
          :total="total"
        >
          <template #default>
            <span class="el-pagination__sizes">
              <el-select
                @change="
                  (val) => {
                    if (/^\d+$/.test(val) && val >= 10 && val <= 100) {
                      handleSizeChange(Number(val))
                    }
                  }
                "
                :modelValue="query.size"
                @update:model-value="
                  (val) => {
                    if (/^\d+$/.test(val) && val >= 10 && val <= 100) {
                      query.size = Number(val)
                    }
                  }
                "
                filterable
                allow-create
                placeholder="请选择"
              >
                <template #label="{ value }"> {{ `${value} 条/页` }} </template>
                <el-option
                  v-for="size in [10, 20, 50, 100]"
                  :key="size"
                  :label="`${size} 条/页`"
                  :value="size"
                />
              </el-select>
            </span>
          </template>
        </el-pagination>
      </div>
    </div>
    <Dialog
      :fullscreen="isFullScreen"
      :title="
        isFormReadOnly ? '详情' : refOp?.name || (form?.hasOwnProperty('id') ? '编辑' : '新增')
      "
      v-model="dialogVisible"
      :width="isMobile ? '90%' : '50%'"
      @closed="dialogClose"
    >
      <RevTableForm
        v-if="formType === 'table'"
        ref="tableForm"
        v-model="form"
        :meta="columns"
        :config="tableFormConfig"
      />
      <grid-form
        v-else-if="dialogVisible && pageForm && pageForm.grid"
        ref="form"
        v-model="form"
        :fields="columns"
        :page="pageForm"
        module="form"
        :readonly="isFormReadOnly"
      />
      <!-- <meta-form v-else-if="dialogVisible" ref="form" v-model="form" :meta="columns" /> -->
      <RevRelations
        ref="relationRef"
        v-if="dialogVisible"
        :relations="relations"
        :currentOp="currentOp"
        :page="config.page"
        :readonly="isFormReadOnly"
        :form="form"
        :mainSource="source"
        :mainData="mainData"
        :mainSelection="useCrossSelection ? crossSelectionList : multipleSelection"
      />
      <template #footer>
        <span class="dialog-footer">
          <template v-if="refOp?.buttons">
            <template v-for="btn in refOp?.buttons" :key="btn.key">
              <el-button
                v-if="btn.type === 'close'"
                size="default"
                @click="dialogVisible = false"
                >{{ btn.name || '取 消' }}</el-button
              >
              <el-button
                v-else-if="btn.type === 'save'"
                type="primary"
                size="default"
                :disabled="isLoading || isBasicLoading || checkDisabled(btn, form)"
                @click="doSave"
                >{{ btn.name || '确 定' }}</el-button
              >
              <el-button
                v-else-if="btn.type === 'delete'"
                type="danger"
                size="default"
                :disabled="isLoading || isBasicLoading || checkDisabled(btn, form)"
                @click="showDelete(form, true)"
                >{{ btn.name || '删 除' }}</el-button
              >
              <el-button
                v-else
                type="primary"
                size="default"
                :disabled="isLoading || isBasicLoading || checkDisabled(btn, form)"
                @click="doOperation(btn, form, true)"
                >{{ btn.name }}</el-button
              >
            </template>
          </template>
          <template v-else>
            <el-button @click="dialogVisible = false">取 消</el-button>
            <el-button type="primary" :disabled="isLoading || isBasicLoading" @click="doSave"
              >确 定</el-button
            >
          </template>
        </span>
      </template>
    </Dialog>
    <Dialog
      :fullscreen="!!formOp?.form?.page?.relations?.length"
      :title="formOp ? formOp.name : ''"
      v-model="formDialogVisible"
      :width="isMobile ? '90%' : '50%'"
      @close="formDialogClose"
    >
      <meta-form
        v-if="formDialogVisible && formOp?.form?.fields"
        ref="revFormRef"
        v-model="form"
        :meta="formFields"
      />
      <RevForm v-else-if="formDialogVisible" ref="revFormRef" :fields="formFields" />
      <RevRelations
        ref="relationRef"
        v-if="formDialogVisible && formOp?.form?.page"
        :relations="formOp?.form?.page?.relations"
        :currentOp="currentOp"
        :page="formOp?.form?.page"
        :readonly="isFormReadOnly"
        :form="form"
        :mainSource="source"
        :mainData="mainData"
      />
      <template #footer>
        <span class="MetaDialog-footer">
          <el-button @click="formDialogVisible = false">取 消</el-button>
          <el-button type="primary" :disabled="isLoading || isBasicLoading" @click="doSaveForm"
            >确 定</el-button
          >
        </span>
      </template>
    </Dialog>
    <Dialog
      :title="iframeOp ? iframeOp.title || iframeOp.name || '&nbsp;' : '&nbsp;'"
      v-model="iframeDialogVisible"
      :fullscreen="true"
      :width="isMobile ? '90%' : '50%'"
      @close="iframeDialogClose"
    >
      <iframe v-if="iframeUrl" width="100%" height="100%" :src="iframeUrl"></iframe>
    </Dialog>
    <Dialog title="订单支付" v-model="qrcodeDialogVisible" width="500px" @close="qrcodeDialogClose">
      <div class="qrcode-container">
        <div class="qrcode-content">
          <Qrcode :text="qrcodeUrl" />
          <div class="qrcode-tip">请使用微信扫码支付</div>
          <div class="qrcode-notice">
            <el-alert title="完成支付后请点击确认按钮" type="warning" :closable="false" show-icon />
          </div>
        </div>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="qrcodeDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmPayment">确认已支付</el-button>
        </span>
      </template>
    </Dialog>
    <Dialog title="已选择" v-model="crossSelectionDialogVisible" :width="isMobile ? '90%' : '50%'">
      <meta-table
        :meta="pageColumns"
        :list="crossSelectionList"
        :showSelect="false"
        :showOperation="true"
        :config="{
          ...tableConfig,
          height: singlePage ? 'auto' : 'unset',
          'max-height': 'unset',
          'operation-width': '80px'
        }"
      >
        <template #operator="scope">
          <el-link type="primary" @click="removeCrossSelection(scope.row)">移除</el-link>
        </template>
      </meta-table>
    </Dialog>
  </div>
</template>

<script>
import { h, defineAsyncComponent } from 'vue'
import {
  getTableMeta,
  getTableList,
  addTableData,
  editTableData,
  deleteTableData,
  exportTableData,
  generalOperation,
  doApi,
  doAjaxDownload,
  getOrderStatus
} from '@/api/basic'
import { MetaForm, MetaTable, MetaQuery } from './meta'
import {
  initRemoteValues,
  initForeignValues,
  getDefaultGridPage,
  getPageTableColumns,
  checkVisible,
  checkDisabled,
  getPermissions,
  hasPermissions,
  downloadLists,
  getGridDefaultValue,
  doPreview
} from './utils/table'
import { Dialog } from './Dialog'
import { ElMessage, ElMessageBox, ElLoading as Loading } from 'element-plus'
import GridForm from './AutoGrid/index.vue'
import RevTableForm from '@/modules/TableForm.vue'
import { debounce, method, intersection, template, get } from 'lodash-es'
import { useBasicStore } from '@/store/modules/basic'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useAppStore } from '@/store/modules/app'
import Qrcode from '@/components/Qrcode/src/Qrcode.vue'
import { getTemplateData } from './AutoGrid/tool'
import dayjs from 'dayjs'
const basicStore = useBasicStore()
const permissionStore = usePermissionStoreWithOut()
const appStore = useAppStore()

import { useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache()
const { wsCache: ssCache } = useCache('sessionStorage')

const modules = import.meta.glob('../views/**/*.{vue,tsx}')

export default {
  name: 'RevTable',
  components: { MetaTable, MetaForm, Dialog, MetaQuery, RevTableForm, GridForm, Qrcode },
  props: {
    config: {
      type: Object,
      default: () => ({})
    },
    showFilter: {
      type: Boolean,
      default: true
    },
    defaultQuery: {
      type: Object,
      default: () => ({})
    },
    singlePage: {
      type: Boolean,
      default: false
    },
    events: {
      // 自定义组件事件
      type: Object
    }
  },
  data() {
    return {
      collapseButtonCount: 3, // 按钮多于3个折叠
      dataList: [],
      total: 0,
      dialogVisible: false,
      columns: [],
      form: {},
      isLoading: false,
      rules: {},
      multipleSelection: [],
      query: this.getSsCachePage(),
      conditions: {},
      queryShow: false,
      loadingInstance: null,
      formOp: null,
      formFields: [],
      formDialogVisible: false,
      orderBy: {},
      genOperations: [],
      formType: 'form',
      refPage: '', // 自定义form弹框
      refOp: null, // 当前点击的按钮
      currentOp: null, // 当前点击的按钮
      tableFormConfig: {},
      iframeUrl: '',
      iframeOp: null,
      iframeDialogVisible: false,
      iframeHealthTimer: null,
      isFormReadOnly: false,
      mainData: [],
      usePressGun: false, // 是否有配置pressGun
      isPressGun: false, // 是否扫码枪操作
      pressGunStr: '', // 扫码枪输入字符串

      // 支付相关数据
      qrcodeDialogVisible: false,
      currentOrderId: null,
      mergeCellsData: [], // 合并单元格
      pageColumns: [], // 自定义列配置
      useCrossSelection: false, // 允许跨页选择
      crossSelectionList: [], // 跨页选择列表
      crossSelectionDialogVisible: false // 预览跨域选择弹框
    }
  },
  computed: {
    source() {
      return this.config?.meta?.source
    },
    operations() {
      return (
        this.config?.page?.operations || [
          ...this.genOperations,
          ...(this.config?.meta?.operations || [])
        ]
      )
    },
    chains() {
      return this.config?.meta?.chains || []
    },
    pageQuery() {
      return {
        ...getDefaultGridPage(this.columns, 'query'),
        ...this.config?.page?.query
      }
    },
    pageForm() {
      return {
        ...getDefaultGridPage(this.columns, 'form'),
        ...((this.refPage && this.config?.page[this.refPage]) || this.config?.page?.form)
      }
    },
    // 自定义请求接口
    customRequest() {
      return { ...this.config?.meta?.request, ...this.config?.page?.request }
    },
    // pageColumns() {
    //   return getPageTableColumns(this.columns, this.config?.page?.table?.fields)
    // },
    relations() {
      return this.config?.page?.relations
        ? [this.config?.page?.relations].flat()
        : this.config?.meta?.page?.relations
        ? [this.config?.meta?.page?.relations].flat()
        : []
    },
    // 扫码枪配置
    pressGun() {
      return this.config?.page?.pressGun
    },
    slotColumns() {
      return this.config?.meta?.slotColumns || []
    },
    tableConfig() {
      const rowBtns = this.operations?.filter((op) => op.byitem && hasPermissions(op?.permissions))
      return {
        'keep-source': true,
        // 'edit-config': {
        //   enabled: false,
        //   trigger: 'dblclick',
        //   mode: 'row',
        //   showStatus: true,
        //   autoClear: true
        // },
        ...this.config?.meta?.config,
        'operation-width': this.isMobile
          ? '80px'
          : `${
              40 +
              (rowBtns.length > this.collapseButtonCount
                ? this.collapseButtonCount
                : rowBtns.length) *
                80
            }px`
      }
    },
    isBasicLoading() {
      return basicStore.getIsLoading
    },
    showOperation() {
      return !!(
        this.operations && this.operations.some((o) => o.byitem && hasPermissions(o?.permissions))
      )
    },
    isMobile() {
      return appStore.getMobile
    },
    showRelations() {
      const opRelations = this.currentOp?.relations
      if (opRelations === false || opRelations?.length === 0) {
        return false
      } else if (opRelations === true || !opRelations) {
        return true
      } else {
        return [opRelations].flat()
      }
    },
    isFullScreen() {
      return this.refOp && this.refOp.hasOwnProperty('fullscreen')
        ? this.refOp.fullscreen
        : !!this.relations?.some(
            (relation) => this.checkShowRelation(relation) && checkVisible(relation, this.form)
          )
    }
  },
  watch: {
    defaultQuery: {
      async handler(val) {
        if (!this.columns?.length) {
          await this.getColumns()
        }
        Object.assign(this.conditions, val)
        // query的默认order
        if (this.pageQuery?.config?.order) {
          this.orderBy = this.pageQuery?.config?.order
        }
        this.$nextTick(() => {
          this.getList(getGridDefaultValue(this.pageQuery, this.conditions))
        })
      },
      immediate: true
    },
    // 跨页选择
    useCrossSelection(val) {
      if (val) {
        this.crossSelectionList = [...this.multipleSelection]
      } else {
        this.crossSelectionList = []
      }
    }
  },
  created() {
    if (this.source) {
      // this.getColumns()
      // this.getList()
    }
  },
  mounted() {
    if (this.$refs.metaTableRef?.tableRef && this.$refs.toolbarRef) {
      this.$refs.metaTableRef?.tableRef.connect(this.$refs.toolbarRef)
    }
  },
  beforeUnmount() {
    document.removeEventListener('keypress', this.keyPress, !this.pressGun?.useSearch)
  },
  deactivated() {
    document.removeEventListener('keypress', this.keyPress, !this.pressGun?.useSearch)
  },
  activated() {
    this.initPressGun()
  },
  methods: {
    // 支付相关方法
    async showPaymentQRCode(order_ids) {
      const token = wsCache.get('Admin-Token')
      const payUrl = basicStore.getProject.pay_url
      this.qrcodeUrl = `${payUrl}?token=${token}&order_ids=${order_ids.join(',')}`
      this.qrcodeDialogVisible = true
    },

    async confirmPayment() {
      try {
        const { code, data, message } = await getOrderStatus(this.currentOrderId)

        if (code == 200) {
          const areAllOrdersPaid = this.currentOrderId.every((orderId) => {
            const orderData = data.find((order) => order.order_id === orderId)
            return orderData && orderData.payStatus === '2'
          })
          if (areAllOrdersPaid) {
            ElMessage.success('支付确认成功')
            this.qrcodeDialogVisible = false
            this.getList()
          } else {
            ElMessage.error('订单未支付')
          }
        } else {
          ElMessage.error(message || '支付确认失败')
        }
      } catch (error) {
        console.error('确认支付失败:', error)
        ElMessage.error('确认支付时发生错误')
      }
    },

    qrcodeDialogClose() {
      this.currentOrderId = null
    },

    async handleBatchPay(key, row) {
      this.multipleSelection = this.multipleSelection.filter((item) => item.payStatus !== '已支付')
      let order_ids
      if (key === 'batch_pay') {
        if (this.multipleSelection.length === 0) {
          ElMessage.warning('请选择要支付的课程，已支付的课程不能重复支付')
          return
        }
        order_ids = this.multipleSelection.map((item) => item.order_id)
      } else {
        order_ids = [row.order_id]
      }
      this.currentOrderId = order_ids
      await this.showPaymentQRCode(order_ids)
    },
    // isSubButtons按钮上配置buttons触发的事件
    async doOperation(op, row, isSubButtons = false) {
      // 如果传入了自定义事件
      if (this.events?.[op.key] && typeof this.events?.[op.key] === 'function') {
        this.events?.[op.key](op, row, this)
        return
      }
      if (!isSubButtons) {
        // 按钮配置有ref使用ref
        this.refPage = op.ref || ''
        this.refOp = op.ref ? op : null
        this.currentOp = op
      }
      if (op.type === 'search') {
        this.getList()
      } else if (op.type === 'route') {
        this.doRoute(row, op)
      } else if (op.type === 'url') {
        this.doUrl(row, op)
      } else if (op.type === 'search_export') {
        this.downloadList('', op)
      } else if (op.type === 'add') {
        this.showAdd()
      } else if (op.type === 'view') {
        this.showView(row, op)
      } else if (op.type === 'edit') {
        this.showEdit(row)
      } else if (op.type === 'delete') {
        this.showDelete(row)
      } else if (op.type === 'form' || op.type === 'table') {
        if (op.key === 'batch_pay' || op.key === 'single_pay') {
          this.handleBatchPay(op.key, row)
        } else {
          this.showForm(row, op)
        }
      } else if (op.type === 'ajax') {
        if (isSubButtons) {
          this.doSubAjax(row, op, true)
        } else {
          this.doAjax(row, op, true)
        }
      } else if (op.type === 'iframe') {
        this.doIframe(row, op)
      } else if (op.type === 'download') {
        this.doDownload(row, op)
      } else if (op.type === 'preview') {
        this.doPreview(row, op)
      } else if (op.type === 'component') {
        this.doCustomComponent(row, op)
      } else if (op.type === 'general_operation' && op.form) {
        this.formOp = op
        this.form = row ? JSON.parse(JSON.stringify(row)) : {}
        if (Array.isArray(op.form)) {
          this.formFields = JSON.parse(JSON.stringify(op.form))
        } else if (op.form) {
          this.formFields = op.form.fields || []
        }
        this.formDialogVisible = true
      } else {
        if (op.loading) {
          this.loadingInstance = Loading.service({ fullscreen: true })
        }
        let data
        this.doComponentConnect(op, row, data, 'before')
        if (op.key) {
          const { data: resData } = await generalOperation({
            key: op.key,
            source: this.source,
            fields: row
          }).catch(() => {
            if (this.loadingInstance) {
              this.loadingInstance.close()
            }
          })
          data = resData
          this.getList()
        }
        this.doComponentConnect(op, row, data, 'after')
        if (this.loadingInstance) {
          this.loadingInstance.close()
        }
      }
    },
    checkDisabled: checkDisabled,
    // 获取能显示的按钮列表
    getShowButtons(row) {
      return (
        this.operations?.filter(
          (op) => op.byitem && hasPermissions(op.permissions) && checkVisible(op, row)
        ) || []
      )
    },
    // 组件之间的交互
    /**
     * "type": "before/after", before：执行general_operation之前调用，此时无data数据，after：执行之后调用，data为general_operation返回的数据
     * "field": "row的filed", 将eval执行的结果赋值给row[field]
     * "fields": ["row的filed"], 将eval执行的结果赋值给row[fields]
     * "eval": "调用组件的方法，包含operation, row, data参数"
     */
    async doComponentConnect(operation, row, data, type) {
      if (!operation.target) {
        return
      }
      Object.keys(operation.target).forEach(async (key) => {
        const target = operation.target[key]
        const comp = this.$parent.$parent.$refs[key]?.[0]
        console.log(this.$parent, target.eval, target.type, type)
        if (comp && target && target.eval && target.type === type) {
          console.log(`comp.${target.eval}`)
          if (type === 'before') {
            const fieldVal = eval(`comp.${target.eval}`)
            if (
              target.fields &&
              target.fields.length > 0 &&
              fieldVal &&
              typeof fieldVal === 'object'
            ) {
              target.fields.forEach(async (field) => {
                row[field] = fieldVal[field] ? fieldVal[field] : row[field]
                await editTableData({ source: this.source, fields: row })
              })
            } else if (target.field && fieldVal) {
              row[target.field] = fieldVal
              await editTableData({ source: this.source, fields: row })
            }
          } else if (type === 'after') {
            eval(`comp.${target.eval}`)
          } else {
            eval(`comp.${target.eval}`)
          }
        }
      })
    },
    // 在执行ajax
    async doChains(trigger, form, row) {
      for (let i = 0; i < this.chains.length; i++) {
        const gen = this.chains[i]
        if ([gen.trigger].flat().includes(trigger)) {
          switch (gen.type) {
            case 'ajax': {
              const url = gen.ajax.url
              if (url) {
                const method = gen.ajax.method || 'get'
                const results = gen.ajax.results || {}
                const params = Object.keys(gen.ajax.params || {}).reduce((t, c) => {
                  try {
                    const compiled = template(gen.ajax.params[c])
                    t[c] = compiled(form)
                    return t
                  } catch (e) {
                    return t
                  }
                }, {})
                await doApi(url, params, method).then(({ data }) => {
                  if (row && data) {
                    Object.keys(results).forEach((field) => {
                      const compiled = template(results[field])
                      row[field] = compiled(data)
                      this.$refs.metaTableRef?.reloadRow(row, field)
                    })
                  }
                })
              }
              break
            }
          }
        }
      }
    },
    async loadData(table = {}) {
      const { fields, data } = table
      console.log('loadData===>', fields, data)
      await initRemoteValues(fields, this.source)
      this.columns = fields
      this.dataList = data
      this.multipleSelection = []
      this.total = 0
    },
    async getList() {
      // 查询条件
      const conditions = this.getConditions({ ...this.conditions, ...this.$route.query })
      const requestData = {
        as_dict: true,
        ...this.query,
        conditions,
        source: this.source,
        filter: this.config?.filter,
        order: this.orderBy,
        service: this.config?.meta?.service || this.config?.page?.query?.service || undefined
      }
      let res = {
        data: []
      }
      // 如果配置自定义查询接口
      if (this.customRequest?.search) {
        const requestConfig =
          typeof this.customRequest?.search === 'string'
            ? {
                url: this.customRequest?.search,
                method: 'post',
                data: requestData,
                headers: {}
              }
            : {
                method: 'post',
                ...this.customRequest?.search,
                data: {
                  ...this.customRequest?.search?.params,
                  ...requestData
                }
              }
        res = await doApi(
          requestConfig.url,
          requestConfig.data,
          requestConfig.method,
          requestConfig.headers
        )
      } else {
        res = await getTableList(requestData)
      }
      // console.log('this.columns:', this.columns)
      initForeignValues(res.data, this.pageColumns).catch(() => {})
      // initRemoteValues(this.columns, this.source)
      this.dataList = res.data || []
      this.mergeCellsData = res.merge_cells_data || []
      this.multipleSelection = this.useCrossSelection
        ? this.dataList.filter((x) => this.crossSelectionList.some((a) => a.id === x.id))
        : []
      this.total = res.count
    },
    async getColumns() {
      console.log('getColumns===>')
      if (this.config && this.config.meta && this.config.meta.fields) {
        const fields = this.config.meta.fields
        // await initRemoteValues(fields, this.source)
        this.columns = fields
      } else {
        const res = await getTableMeta({
          source: this.source
        })
        // await initRemoteValues(res.fields, this.source)
        this.columns = res.fields
        this.genOperations = res.operations
      }
      this.pageColumns = getPageTableColumns(this.columns, this.config?.page?.table?.fields)
      this.initPressGun()
      this.rules = this.columns.reduce((t, c) => {
        if (c.editable && c.require) {
          t[c.key] = [
            {
              required: true,
              message: (c.type === 'enum' ? '请选择' : '请输入') + c.name,
              trigger: 'blur'
            }
          ]
        }
        return t
      }, {})
    },
    doRoute(row, op) {
      if (op.pattern) {
        this.$router.push(getTemplateData(op.pattern, row))
      } else {
        this.$router.push(getTemplateData(op.route, row))
      }
    },
    doUrl(row, op) {
      if (op.pattern) {
        const url = getTemplateData(op.pattern, row)
        const a = document.createElement('a')
        a.target = '_blank'
        a.href = url
        a.click()
      } else {
        const route = this.$router.resolve(getTemplateData(op.route, row))
        const a = document.createElement('a')
        a.target = '_blank'
        a.href = route.href
        a.click()
      }
    },
    // 点击按钮执行一个ajax
    // ajax: {
    //   url: "/basic/getConfigs",
    //   params: {
    //     partnumber: "${取行字段数据}"
    //   },
    //   results: {
    //     partnumber: "${可以取返回结果字段，赋值给行字段}"
    //   },
    //   confirm: "请使用${可以取返回结果字段}",
    //   message: "成功${可以取返回结果字段}"
    // }
    // isOp 是按钮上执行的ajax
    async doAjax(row, op, isOp = false, customData) {
      return new Promise(async (resolve) => {
        const isBatch = !row || (op.bytable && !op.byitem)
        // const op = {
        //   ajax: {
        //     url: "/basic/getConfigs",
        //     params: {
        //       partnumber: "${partnumber}"
        //     },
        //     results: {
        //       partnumber: "${title}"
        //     },
        //     confirm: "请使用${title}",
        //     message: "成功${title}",
        //     messagebox: "成功${title}"
        //   }
        // }
        const url = op.ajax.url
        if (url) {
          row =
            row || (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection)[0]
          let confirm = op.ajax.confirm || ''
          // 配置有config弹出确认框
          if (confirm) {
            try {
              const compiled = template(confirm)
              confirm = compiled(row || {})
            } catch (e) {}
            const isConfirmed = await new Promise((resolve, reject) => {
              ElMessageBox.confirm(`${confirm}`, '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
              })
                .then(() => {
                  resolve(true)
                })
                .catch(() => {
                  reject(false)
                })
            })
            if (!isConfirmed) {
              return
            }
          }
          const method = op.ajax.method || 'get'
          const results = op.ajax.results // 执行成功后，修改row数据
          const message = op.ajax.message || '' // 执行成功后，提示信息
          const messagebox = op.ajax.messagebox || '' // 执行成功后，弹出一个提示框
          const params =
            customData ||
            (op.ajax.params
              ? Object.keys(op.ajax.params || {}).reduce(
                  (t, c) => {
                    try {
                      const compiled = template(op.ajax.params[c])
                      t[c] = compiled(row)
                      return t
                    } catch (e) {
                      return t
                    }
                  },
                  this.refOp?.ajax ? row : {}
                )
              : { ...row })
          this.isLoading = true
          if (op.bytable && isBatch) {
            const ids = (
              this.useCrossSelection ? this.crossSelectionList : this.multipleSelection
            ).map((x) => x.id)
            params.ids = ids?.length ? ids : undefined
          }
          await doApi(url, params, method)
            .then((data) => {
              // form执行的ajax
              if (this.refOp?.ajax && isOp) {
                this.dialogVisible = false
              }
              // 如果配置了result就不查询
              if (row && data && results) {
                Object.keys(results).forEach((field) => {
                  const compiled = template(results[field])
                  row[field] = compiled(data)
                  this.$refs.metaTableRef?.reloadRow(row, field)
                })
              } else if (isOp) {
                this.getList()
              }
              // 提示信息
              if (data && message) {
                ElMessage.success(getTemplateData(message, data))
              }
              if (messagebox) {
                if (messagebox === 'component') {
                  this.doCustomComponent(row, op)
                } else {
                  ElMessageBox({
                    title: '',
                    message: getTemplateData(messagebox, data),
                    showConfirmButton: false,
                    showCancelButton: false
                  })
                }
              }
              resolve(data)
            })
            .finally(() => {
              this.isLoading = false
            })
        }
      })
    },
    // 执行一个ajax
    async execAjax(ajax, row, customData) {
      const url = ajax.url
      const method = ajax.method || 'get'
      const params = ajax.params
        ? Object.keys(ajax.params || {}).reduce(
            (t, c) => {
              try {
                t[c] = template(ajax.params[c])(row)
                return t
              } catch (e) {
                return t
              }
            },
            { ...customData }
          )
        : row
      return doApi(url, params, method)
    },
    // 按钮上配置buttons里配置的ajax
    async doSubAjax(row, op, isOp = false, customData) {
      let data = {}
      if (this.refOp?.ref) {
        const valid = await new Promise((resolve) => {
          this.$refs.form.validate((valid) => {
            resolve(valid)
          })
        })
        if (valid) {
          data = { ...this.form }
        }
      }
      if (this.$refs.relationRef) {
        const resList = this.$refs.relationRef.getRelationSelections()
        resList.forEach((res) => {
          if (res.selections?.length && res.key) {
            data[res.key] = res.selections.map((x) => x.id)
          }
        })
      }
      this.execAjax(op.ajax, data, data)
    },
    async doIframe(row, op) {
      if (op.iframe?.open) {
        const res = await this.execAjax(op.iframe.open, row)
        const results = op.iframe?.open?.results || { url: '${url}' }
        this.iframeUrl = template(results.url)(res.data)
        if (op.iframe?.health) {
          if (this.iframeHealthTimer) {
            clearInterval(this.iframeHealthTimer)
          }
          op.iframe.tempRow = row
          op.iframe.tempData = res.data
          const time = template(op.iframe?.health?.time || '10')(res.data)
          this.iframeHealthTimer = setInterval(() => {
            this.execAjax(
              op.iframe.health,
              op.iframe.health?.params ? { ...row, ...res.data } : res.data
            )
          }, (time || 1) * 1000)
          window.onbeforeunload = this.iframeDialogClose
        }
      } else if (op.iframe?.url) {
        this.iframeUrl = op.iframe?.url
      }
      this.iframeOp = op
      this.iframeDialogVisible = true
    },
    async showAdd() {
      this.formType = 'form'
      this.form = {}
      this.dialogVisible = true
    },
    async showForm(item, op) {
      console.log(item, op)
      if (
        !item &&
        op.bytable &&
        op.byitem &&
        (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection).length < 1
      ) {
        ElMessage({
          type: 'info',
          message: '请选择一条记录'
        })
        return
      }
      const itemData =
        item || (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection)[0] || {}
      // const form = this.columns.reduce((t, c) => {
      //   if (c.editable || c.key === 'id') {
      //     t[c.key] = itemData[c.key]
      //   }
      //   return t
      // }, {})
      this.formType = op.type
      this.form = JSON.parse(JSON.stringify(itemData))
      this.tableFormConfig = op.layout
      this.dialogVisible = true
    },
    async showView(item) {
      if (
        !item &&
        (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection).length < 1
      ) {
        ElMessage({
          type: 'info',
          message: '请选择一条记录'
        })
        return
      }
      const itemData =
        item || (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection)[0]
      // const form = this.columns.reduce((t, c) => {
      //   if (c.editable || c.key === 'id') {
      //     t[c.key] = itemData[c.key]
      //   }
      //   return t
      // }, {})
      this.isFormReadOnly = true
      this.formType = 'form'
      this.form = JSON.parse(JSON.stringify(itemData))
      this.dialogVisible = true
    },
    async showEdit(item) {
      if (
        !item &&
        (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection).length < 1
      ) {
        ElMessage({
          type: 'info',
          message: '请选择一条记录'
        })
        return
      }
      const itemData =
        item || (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection)[0]
      // const form = this.columns.reduce((t, c) => {
      //   if (c.editable || c.key === 'id') {
      //     t[c.key] = itemData[c.key]
      //   }
      //   return t
      // }, {})
      this.formType = 'form'
      this.form = JSON.parse(JSON.stringify(itemData))
      this.dialogVisible = true
    },
    async showDelete(item) {
      console.log(this.operations)
      if (
        !item &&
        (this.useCrossSelection ? this.crossSelectionList : this.multipleSelection).length < 1
      ) {
        ElMessage({
          type: 'info',
          message: '请选择要删除的记录'
        })
        return
      }
      ElMessageBox.confirm(`确认删除选择的记录?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(async () => {
          const allItems = item
            ? [item]
            : this.useCrossSelection
            ? this.crossSelectionList
            : this.multipleSelection
          // console.log(allItems)
          // await Promise.all(
          //   allItems.map((t) =>
          //     deleteTableData({
          //       id: [t.id],
          //       source: this.source
          //     })
          //   )
          // )
          await deleteTableData({
            id: allItems.map((t) => t.id),
            source: this.source
          })
          this.getList()
          ElMessage({
            type: 'success',
            message: '删除成功!'
          })
        })
        .catch(() => {})
    },
    // 弹框一个自定义组件页面（需放在./views/目录下）
    async doCustomComponent(row, op) {
      if (op?.component?.module) {
        let compTitle = op?.component?.title || ''
        if (compTitle) {
          try {
            compTitle = template(compTitle)(row || {})
          } catch (e) {}
        }
        const MyAsyncComponent = defineAsyncComponent(
          modules[`../views/${op.component.module}.vue`]
        )
        ElMessageBox({
          title: compTitle,
          ...op?.component?.props,
          message: () =>
            h(MyAsyncComponent, {
              ...op?.component?.props,
              data: row,
              mainSelection: this.useCrossSelection
                ? this.crossSelectionList
                : this.multipleSelection,
              op,
              meta: this.columns,
              onClose: (bool) => {
                ElMessageBox.close()
                if (bool) {
                  this.getList()
                }
              }
            }),
          customStyle: op?.component?.style,
          customClass: 'custom-component',
          showConfirmButton: false,
          showCancelButton: false
        })
      }
    },
    // 点击按钮执行一个文件预览
    async doPreview(row, op) {
      let previewContent = ''
      // 配置了ajax
      if (op.ajax) {
        const url = op.ajax.url
        const method = op.ajax.method || 'get'
        if (!row && this.multipleSelection?.length) {
          row = this.multipleSelection[0]
        }
        const params = op.ajax.params
          ? Object.keys(op.ajax.params || {}).reduce((t, c) => {
              try {
                const compiled = template(op.ajax.params[c])
                t[c] = compiled(row)
                return t
              } catch (e) {
                return t
              }
            }, {})
          : row
        if (op.bytable && !row) {
          params.ids = this.multipleSelection.map((item) => item.id)
        }
        // 配置了pattern去返回内容
        if (op.ajax.pattern) {
          this.isLoading = true
          await doApi(url, params, method)
            .then((res) => {
              console.log(res)
              previewContent = getTemplateData(op.ajax.pattern, res)
            })
            .finally(() => {
              this.isLoading = false
            })
        } else {
          // 未配置pattern认为ajax返回二进制文件
          this.isLoading = true
          await doAjaxDownload(url, params, method)
            .then((res) => {
              const blob = new Blob([res.data])
              const downloadUrl = window.URL.createObjectURL(blob)
              previewContent = downloadUrl
            })
            .finally(() => {
              this.isLoading = false
            })
        }
      } else {
        previewContent = getTemplateData(op.pattern, row)
      }
      doPreview(previewContent, op.preview)
    },
    // 点击按钮执行一个ajax导出文件
    // ajax: {
    //   url: "/basic/getConfigs",
    //   params: {
    //     partnumber: "${取行字段数据}"
    //   },
    //   confirm: "请使用${可以取返回结果字段}",
    //   filename: "文件名${可以取返回结果字段}.xlsx"
    // }
    async doDownload(row, op) {
      let url = op.ajax.url
      if (url) {
        url = getTemplateData(url, row)
        let isBatch = false
        if (op.bytable && !row) {
          const multipleSelection = this.useCrossSelection
            ? this.crossSelectionList
            : this.multipleSelection
          // const allIds = this.multipleSelection.map((item) => item.id);
          row = multipleSelection[0]
          isBatch = multipleSelection.length > 1 ? true : false
        }
        let confirm = op.ajax.confirm || ''
        // 配置有config弹出确认框
        if (confirm) {
          try {
            const compiled = template(confirm)
            confirm = compiled(row || {})
          } catch (e) {}
          const isConfirmed = await new Promise((resolve, reject) => {
            ElMessageBox.confirm(`${confirm}`, '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            })
              .then(() => {
                resolve(true)
              })
              .catch(() => {
                reject(false)
              })
          })
          if (!isConfirmed) {
            return
          }
        }
        if (isBatch) {
          const multipleSelection = this.useCrossSelection
            ? this.crossSelectionList
            : this.multipleSelection
          for (let i = 0; i < multipleSelection.length; i++) {
            const row = multipleSelection[i]
            const method = op.ajax.method || 'get'
            const params = op.ajax.params
              ? Object.keys(op.ajax.params || {}).reduce(
                  (t, c) => {
                    try {
                      const compiled = template(op.ajax.params[c])
                      t[c] = compiled(row)
                      return t
                    } catch (e) {
                      return t
                    }
                  },
                  this.refPage !== 'form' && this.refOp ? row : {}
                )
              : row
            this.isLoading = true
            let fileName = `${Date.now()}.xlsx`
            if (op.ajax.filename) {
              try {
                fileName = template(op.ajax.filename)({ ...row, $dayjs: dayjs })
              } catch (e) {}
            }
            await doAjaxDownload(url, params, method)
              .then((res) => {
                if (!op.ajax.filename) {
                  const contentDisposition = res.headers['content-disposition']
                  if (contentDisposition && contentDisposition.includes('filename')) {
                    const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
                    const matches = filenameRegex.exec(contentDisposition)
                    if (matches != null && matches[1]) {
                      const metchName = matches[1].replace(/['"]/g, '') // 去除引号
                      fileName = decodeURIComponent(metchName.replace('utf-8', ''))
                    }
                  }
                }
                const blob = new Blob([res.data])
                const downloadUrl = window.URL.createObjectURL(blob)
                const a = document.createElement('a')
                a.style.display = 'none'
                a.href = downloadUrl
                a.download = fileName // 设置下载文件的默认名
                document.body.appendChild(a)
                a.click()
                window.URL.revokeObjectURL(downloadUrl) // 释放 URL 对象
                document.body.removeChild(a)
              })
              .finally(() => {})
          }
          this.isLoading = false
          this.dialogVisible = false
        } else {
          const method = op.ajax.method || 'get'
          const params = op.ajax.params
            ? Object.keys(op.ajax.params || {}).reduce(
                (t, c) => {
                  try {
                    const compiled = template(op.ajax.params[c])
                    t[c] = compiled(row)
                    return t
                  } catch (e) {
                    return t
                  }
                },
                this.refPage !== 'form' && this.refOp ? row : {}
              )
            : row
          this.isLoading = true
          let fileName = `${Date.now()}.xlsx`
          if (op.ajax.filename) {
            try {
              fileName = template(op.ajax.filename)({ ...row, $dayjs: dayjs })
            } catch (e) {}
          }
          await doAjaxDownload(url, params, method)
            .then((res) => {
              if (!op.ajax.filename) {
                const contentDisposition = res.headers['content-disposition']
                if (contentDisposition && contentDisposition.includes('filename')) {
                  const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
                  const matches = filenameRegex.exec(contentDisposition)
                  if (matches != null && matches[1]) {
                    const metchName = matches[1].replace(/['"]/g, '') // 去除引号
                    fileName = decodeURIComponent(metchName.replace('utf-8', ''))
                  }
                }
              }
              const blob = new Blob([res.data])
              const downloadUrl = window.URL.createObjectURL(blob)
              const a = document.createElement('a')
              a.style.display = 'none'
              a.href = downloadUrl
              a.download = fileName // 设置下载文件的默认名
              document.body.appendChild(a)
              a.click()
              window.URL.revokeObjectURL(downloadUrl) // 释放 URL 对象
              document.body.removeChild(a)
            })
            .finally(() => {
              this.isLoading = false
              this.dialogVisible = false
            })
        }
      }
    },
    async doSaveForm() {
      this.$refs.revFormRef?.validate(async (valid) => {
        if (!valid) {
          return
        }
        const relationTable = this.$refs.relationRef
        if (relationTable && !(await relationTable.editValidate())) {
          return
        }
        this.isLoading = true
        if (this.formOp.loading) {
          this.loadingInstance = Loading.service({ fullscreen: true })
        }
        if (this.formOp?.form?.ajax) {
          const ajax = this.formOp?.form?.ajax || {}
          const url = ajax.url
          if (url) {
            const method = ajax.method || 'get'
            const results = ajax.results || {}
            const params = {
              ...Object.keys(ajax.params || {}).reduce((t, c) => {
                try {
                  const compiled = template(ajax.params[c])
                  t[c] = compiled(this.form)
                  return t
                } catch (e) {
                  return t
                }
              }, {}),
              ...this.form
            }
            // 多选
            if (this.formOp.bytable && !this.form.id) {
              params.selectDatas = this.useCrossSelection
                ? this.crossSelectionList
                : this.multipleSelection
            }
            // 带有上传文件
            if (this.formFields.some((field) => field.type === 'blob' && params[field.key])) {
              const formdata = new FormData()
              Object.keys(params).forEach((key) => {
                formdata.append(key, params[key])
              })
              await doApi(url, formdata, method, { 'Content-Type': 'multipart/form-data' })
                .then(({ data }) => {
                  console.log(data)
                })
                .catch(() => {
                  this.isLoading = false
                  if (this.loadingInstance) {
                    this.loadingInstance.close()
                  }
                })
            } else {
              await doApi(url, params, method)
                .then(({ data }) => {
                  console.log(data)
                })
                .catch(() => {
                  this.isLoading = false
                  if (this.loadingInstance) {
                    this.loadingInstance.close()
                  }
                })
            }
          }
          this.getList()
        } else {
          const { data } = await generalOperation({
            key: this.formOp.key,
            source: this.source,
            form: this.formFields
          }).catch(() => {
            this.isLoading = false
          })
          if (data) {
            this.dataList = data
            this.multipleSelection = []
            this.crossSelectionList = []
          } else {
            this.getList()
          }
        }
        if (this.loadingInstance) {
          this.loadingInstance.close()
        }
        ElMessage({ type: 'success', message: '保存成功' })
        this.formDialogVisible = false
        this.isLoading = false
      })
    },
    onCellOperation(col, row) {
      const operation = col.operation
      if (typeof operation === 'string') {
        const findOp = this.operations.find((op) => op.key === operation)
        this.doOperation(findOp, row)
      } else if (operation) {
        this.doOperation(operation, row)
      }
    },
    removeCrossSelection(row) {
      const findIdx = this.crossSelectionList.findIndex((c) => c.id === row.id)
      if (findIdx > -1) {
        this.crossSelectionList.splice(findIdx, 1)
      }
      const multiIdx = this.multipleSelection.findIndex((c) => c.id === row.id)
      if (multiIdx > -1) {
        this.multipleSelection.splice(multiIdx, 1)
      }
    },
    multipleSelectionChange(records, oldRecords, checked) {
      if (checked) {
        const addRecords = records.filter(
          (x) => !this.crossSelectionList.some((c) => c.id === x.id)
        )
        this.crossSelectionList.push(...addRecords)
      } else {
        const removedRecords = oldRecords.filter((o) => !records.some((c) => c.id === o.id))
        removedRecords.forEach((r) => {
          const findIdx = this.crossSelectionList.findIndex((c) => c.id === r.id)
          if (findIdx > -1) {
            this.crossSelectionList.splice(findIdx, 1)
          }
        })
      }
    },
    sortChange({ field, order }) {
      if (order) {
        this.orderBy = { [field]: order }
      } else {
        this.orderBy = {}
      }
      this.getList()
    },
    async editSave(form, row) {
      this.isLoading = true
      await editTableData({ source: this.source, fields: form }).finally(() => {
        this.isLoading = false
      })
      await this.doChains('afterUpdate', form, row)
      ElMessage({ type: 'success', message: '保存成功' })
    },
    async doSave() {
      const validateForm = this.$refs.form || this.$refs.tableForm
      validateForm.validate(async (valid) => {
        if (valid) {
          const relationTable = this.$refs.relationRef
          if (relationTable && !(await relationTable.editValidate())) {
            return
          }
          this.isLoading = true
          if (this.refOp?.ajax) {
            const relationList = this.$refs.relationRef?.getRelationDatas()
            // 有子表关联的按新格式
            if (relationList?.length) {
              const mainParams = this.refOp.ajax.params
                ? Object.keys(this.refOp.ajax.params || {}).reduce(
                    (t, c) => {
                      try {
                        const compiled = template(this.refOp.ajax.params[c])
                        t[c] = compiled(this.form)
                        return t
                      } catch (e) {
                        return t
                      }
                    },
                    this.refOp?.ajax ? this.form : {}
                  )
                : { ...this.form }
              const allData = {
                main: mainParams,
                relations: relationList.map((relation) => ({
                  source: relation.source,
                  data: relation.allList.map((d) => ({
                    ...d,
                    id: /^row_\d+$/.test(d.id) ? undefined : d.id // /^row_\d+$/ 格式的是新增的数据
                  }))
                }))
              }
              const res = await this.doAjax(this.form, this.refOp, false, allData)
              this.getList()
              ElMessage({ type: 'success', message: '保存成功' })
              this.dialogVisible = false
              this.isLoading = false
              return
            } else {
              // 配置有ajax的，有子表的也只走ajax
              const res = await this.doAjax(this.form, this.refOp)
              if (res?.id?.[0]) {
                this.form.id = res?.id?.[0]
              }
            }
          } else if (this.form.id) {
            await editTableData({
              source: this.source,
              fields: this.form
            }).catch(() => {
              this.isLoading = false
            })
            await this.doChains('afterUpdate', this.form)
          } else {
            const batchUpload = this.columns.find((col) => col.batch_upload)
            const batchList = this.$refs.form.getBatchList()
            if (batchList.length > 0 && batchUpload) {
              await Promise.all(
                batchList
                  .filter((bt) => bt.data?.upload_url)
                  .map((bt) => {
                    const form = JSON.parse(JSON.stringify(this.form))
                    form[batchUpload.key] = bt.data
                    return addTableData({ source: this.source, fields: form })
                  })
              )
            } else {
              const res = await addTableData({
                source: this.source,
                fields: this.form
              }).catch(() => {
                this.isLoading = false
              })
              if (res?.id?.[0]) {
                this.form.id = res?.id?.[0]
              }
              await this.doChains('afterAdd', this.form)
            }
          }
          const relationList = this.$refs.relationRef?.getRelationDatas()
          const allPromise = relationList?.map((relation) => [
            relation.insertList?.map((form) => {
              const foreingParams = {}
              const foreignKey = relation.foreign_key
              if (typeof foreignKey === 'string') {
                foreingParams[relation.foreign_key] = this.form.id
              } else if (foreignKey && typeof foreignKey === 'object') {
                Object.keys(foreignKey).forEach((key) => {
                  try {
                    foreingParams[key] = template(foreignKey[key])(this.form)
                  } catch (e) {}
                })
              }
              return addTableData({
                source: relation.source,
                fields: {
                  ...form,
                  id: undefined,
                  ...foreingParams
                }
              })
            }),
            relation.updateList?.map((form) =>
              editTableData({ source: relation.source, fields: form })
            ),
            relation.removeList?.map((form) =>
              deleteTableData({ source: relation.source, id: [form.id] })
            )
          ])
          if (allPromise?.flat().flat().length) {
            await Promise.all(allPromise.flat().flat())
            this.$refs.relationRef?.initColumns()
          }
          this.getList()
          ElMessage({ type: 'success', message: '保存成功' })
          this.dialogVisible = false
          this.isLoading = false
        }
      })
    },
    // 导出excel
    downloadList(command, op) {
      // 根据查询条件导出所有数据
      if (command === 'all') {
        this.isLoading = true
        op._loading = true
        exportTableData({
          conditions: this.getConditions(this.conditions),
          source: this.source
        })
          .then((res) => {
            window.location.href = res.download_url
          })
          .finally(() => {
            setTimeout(() => {
              this.isLoading = false
              op._loading = false
            }, 500)
          })
      } else if (command === 'select') {
        // 导出选中的数据
        downloadLists(
          this.columns,
          this.useCrossSelection ? this.crossSelectionList : this.multipleSelection
        )
      } else {
        // 导出当前页
        downloadLists(this.columns, this.dataList)
      }
    },
    onUploadSuccess() {
      this.getList()
    },
    getConditions(conds) {
      const result = {}
      Object.keys(conds).forEach((key) => {
        // 过滤掉空数组，undefined, '', null 等查询条件
        if (
          (conds[key] && Array.isArray(conds[key]) && conds[key].join('').length < 1) ||
          [undefined, '', null].includes(conds[key])
        ) {
        } else {
          result[key] = conds[key]
        }
      })
      return result
    },
    resetQuery() {
      this.$refs.queryMeta.resetForm()
      // this.conditions = {};
    },
    iframeDialogClose() {
      if (this.iframeOp?.iframe?.close) {
        this.execAjax(
          this.iframeOp?.iframe?.close,
          this.iframeOp?.iframe?.params
            ? { ...this.iframeOp?.iframe?.tempRow, ...this.iframeOp?.iframe?.tempData }
            : this.iframeOp?.iframe?.tempData
        )
      }
      if (this.iframeHealthTimer) {
        clearInterval(this.iframeHealthTimer)
      }
      window.onbeforeunload = null
    },
    formDialogClose() {
      this.form = {}
      this.formOp = null
      this.formFields = []
    },
    dialogClose() {
      const validForm = this.$refs.form || this.$refs.tableForm
      if (validForm) {
        validForm.resetFields()
      }
      this.refPage = ''
      this.refOp = null
      this.currentOp = null
      this.isFormReadOnly = false
      this.form = {}
    },
    handleSelectionChange(val) {
      this.multipleSelection = val
    },
    handleSizeChange(val) {
      this.query.page = 1
      this.query.size = val
      this.getList().finally(() => {
        this.saveSsCachePage()
      })
    },
    handleCurrentChange(val) {
      this.query.page = val
      this.getList().finally(() => {
        this.saveSsCachePage()
      })
    },
    // noCache页面，每次分页时，保存分页信息
    saveSsCachePage(query) {
      const noCache = this.$route.meta?.noCache
      if (noCache) {
        const fullPath = this.$route.fullPath
        let obj = {}
        const paginationCache = ssCache.get('Pagination-Cache')
        if (paginationCache) {
          obj = JSON.parse(paginationCache) || {}
        }
        obj[fullPath] = {
          page: this.query.page,
          size: this.query.size
        }
        ssCache.set('Pagination-Cache', JSON.stringify(obj))
      }
    },
    checkVisible: checkVisible,
    // 验证是否有展示子表
    checkShowRelation(relation) {
      if (typeof this.showRelations === 'boolean') {
        return this.showRelations
      }
      return this.showRelations.includes(relation.key || relation.source)
    },
    // noCache页面，初始化时使用缓存分页信息
    getSsCachePage() {
      const noCache = this.$route.meta?.noCache
      const defaultPage = {
        page: 1,
        size: 20
      }
      if (noCache) {
        const fullPath = this.$route.fullPath
        const paginationCache = ssCache.get('Pagination-Cache')
        if (paginationCache) {
          const obj = JSON.parse(paginationCache)
          return obj?.[fullPath] || defaultPage
        }
        return defaultPage
      }
      return defaultPage
    },
    getPermissions(permissions) {
      return getPermissions(permissions)
    },
    hasPermissions(permissions) {
      return hasPermissions(permissions)
    },
    // 初始化扫码枪操作
    initPressGun() {
      this.usePressGun = !!this.pressGun
      document.removeEventListener('keypress', this.keyPress, !this.pressGun?.useSearch)
      if (this.usePressGun) {
        document.addEventListener('keypress', this.keyPress, !this.pressGun?.useSearch)
      }
    },
    resetPress: debounce(function () {
      this.isPressGun = false
      this.pressGunStr = ''
    }, 150),
    // 执行扫码枪结束方法
    async doPressEnter(str) {
      console.log('doPressEnter===', str)
      if (str && this.pressGun) {
        let hasMatch = false // 是否匹配过
        // 可以配置多个
        const pressGuns = [this.pressGun].flat()
        // 解析输入串
        pressGuns.forEach(async (pressGunConfig) => {
          // 一旦匹配过一次，就不再继续匹配
          if (hasMatch) {
            return
          }
          const inputStr = [str]
          if (pressGunConfig.inputTest) {
            const regExp = new RegExp(pressGunConfig.inputTest)
            if (regExp.test(str)) {
              hasMatch = !pressGunConfig.useNext
              // 配置有match进行解析
              if (pressGunConfig.inputMatch) {
                const regExp = new RegExp(pressGunConfig.inputMatch)
                const strs = str.match(regExp) || []
                inputStr.push(...strs.splice(1))
              } else if (pressGunConfig.inputSplit) {
                const strs = str.split(new RegExp(pressGunConfig.inputSplit))
                inputStr.push(...strs)
              }
            } else {
              return
            }
          } else {
            // 配置有match进行解析
            if (pressGunConfig.inputMatch) {
              const regExp = new RegExp(pressGunConfig.inputMatch)
              const strs = str.match(regExp) || []
              inputStr.push(...strs.splice(1))
            } else if (pressGunConfig.inputSplit) {
              const strs = str.split(new RegExp(pressGunConfig.inputSplit))
              inputStr.push(...strs)
            }
          }
          const formKey = pressGunConfig.form || 'conditions'
          const inputData = { ...this[formKey], inputStr }
          console.log('---------', inputStr, inputData)
          if (pressGunConfig.results) {
            Object.keys(pressGunConfig.results).forEach((field) => {
              const compiled = template(pressGunConfig.results[field])
              this[formKey][field] = compiled(inputData)
            })
          }
          if (pressGunConfig.ajax) {
            await this.doAjax(inputData, pressGunConfig)
          }
          if (pressGunConfig.useSearch) {
            this.getList()
          }
          if (pressGunConfig.resetSearch) {
            this.resetQuery()
          }
        })
      }
    },
    keyPress(e) {
      // 获取按键的键码
      var keyCode = e.keyCode || e.which
      // 将键码转换为字符串
      var keyString = String.fromCharCode(keyCode)
      if (e.code === 'Enter') {
        this.doPressEnter(this.pressGunStr)
      } else if (keyString) {
        this.isPressGun = true
        this.pressGunStr += keyString
        this.resetPress()
      }
    }
  }
}
</script>

<style lang="less" scoped>
.rev-table {
  width: 100%;
  height: 100%;
  position: relative;

  .table-title {
    padding-bottom: 10px;
    font-weight: bold;
  }

  .query-tools {
    margin-bottom: 10px;
    // display: flex;
    // justify-content: space-between;

    :deep(.vxe-toolbar) {
      padding: 0;
    }

    .op-btn {
      padding: 8px 15px;
      img {
        height: 13px;
        line-height: 0;
        margin-right: 5px;
        vertical-align: middle;
      }
      .text {
        vertical-align: middle;
      }
    }

    .query-upload {
      display: inline-block;
      vertical-align: middle;
      margin-right: 10px;
      margin-left: 10px;
    }
  }

  .text-btn {
    padding: 3px 5px;
    margin-left: 0;

    img {
      height: 14px;
      vertical-align: middle;
      margin-right: 3px;
    }
    .text {
      vertical-align: middle;
    }
  }

  .table-pagination {
    margin-top: 10px;
    text-align: right;
  }

  .qrcode-container {
    padding: 20px;
    text-align: center;

    .qrcode-content {
      .qrcode-image {
        width: 200px;
        height: 200px;
        margin-bottom: 15px;
      }

      .qrcode-amount {
        font-size: 18px;
        font-weight: bold;
        color: #303133;
        margin-bottom: 10px;
      }

      .qrcode-tip {
        color: #909399;
        font-size: 14px;
        margin-bottom: 15px;
      }

      .qrcode-notice {
        margin-top: 20px;
        .el-alert {
          display: inline-block;
          padding: 8px 16px;
        }
      }
    }
  }
}
</style>
